#include "layout.h"
#include "core.h"

namespace core
{



/*
 * @brief: layout functions
 */
bool LayoutHelper::GetBackgroundRect( const FaceStyle &style, const rect &border, rect &background )
{
    background.empty();

    background.left = border.left + style.border_width_;
    background.right = border.right - style.border_width_;
    background.top = border.top + style.border_width_;
    background.bottom = border.bottom - style.border_width_;

    if( !background.valid() )
    {
        background.empty();
        return false;
    }

    return true;
}

dword LayoutHelper::GetFitType( const FaceStyle &style )
{
    dword fit = FIT_TYPE_NULL;

    if( style.Default( FaceStyle::PROPERTY_WIDTH ) )
    {
        fit |= FIT_TYPE_WIDTH;
    }

    if( style.Default( FaceStyle::PROPERTY_HEIGHT ) )
    {
        fit |= FIT_TYPE_HEIGHT;
    }

    return fit;
}

dword LayoutHelper::GetPositionType( const FaceStyle &style )
{
    if( !style.Default( FaceStyle::PROPERTY_LEFT ) &&
        !style.Default( FaceStyle::PROPERTY_TOP ) )
    {
        return POSITION_TYPE_LEFT_TOP;
    }
    
    if( !style.Default( FaceStyle::PROPERTY_LEFT ) &&
        !style.Default( FaceStyle::PROPERTY_BOTTOM ) )
    {
        return POSITION_TYPE_LEFT_BOTTOM;
    }
    
    if( !style.Default( FaceStyle::PROPERTY_RIGHT ) &&
        !style.Default( FaceStyle::PROPERTY_TOP ) )
    {
        return POSITION_TYPE_RIGHT_TOP;
    }

    if( !style.Default( FaceStyle::PROPERTY_RIGHT ) &&
        !style.Default( FaceStyle::PROPERTY_BOTTOM ) )
    {
        return POSITION_TYPE_RIGHT_BOTTOM;
    }

    if( !style.Default( FaceStyle::PROPERTY_LEFT ) )
    {
        return POSITION_TYPE_LEFT_TOP;
    }

    if( !style.Default( FaceStyle::PROPERTY_RIGHT ) )
    {
        return POSITION_TYPE_RIGHT_TOP;
    }

    if( !style.Default( FaceStyle::PROPERTY_TOP ) )
    {
        return POSITION_TYPE_LEFT_TOP;
    }

    if( !style.Default( FaceStyle::PROPERTY_BOTTOM ) )
    {
        return POSITION_TYPE_LEFT_BOTTOM;
    }

    return POSITION_TYPE_LEFT_TOP;
}

int LayoutHelper::GetBaseline( FaceWindow &control, bool exclude )
{
    int baseline = 0;
    FaceWindow *iterator = control.PreviousSibling();
    while( iterator && !iterator->IsNewLine() )
    {
        baseline = max( baseline, iterator->GetBoundaryRect().bottom );
        iterator = iterator->PreviousSibling();
    }

    if( !exclude )
    {
        baseline = max( baseline, control.GetBoundaryRect().bottom );
    }

    iterator = control.NextSibling();
    while( iterator && !iterator->IsNewLine() )
    {
        baseline = max( baseline, iterator->GetBoundaryRect().bottom );
        iterator = iterator->NextSibling();
    }

    return baseline;
}

int LayoutHelper::GetColumnline( FaceWindow &control, bool exclude )
{
    int columnline = 0;
    FaceWindow *iterator = control.PreviousSibling();
    while( iterator )
    {
        columnline = max( columnline, iterator->GetBoundaryRect().right );
        iterator = iterator->PreviousSibling();
    }

    if( !exclude )
    {
        columnline = max( columnline, control.GetBoundaryRect().right );
    }

    iterator = control.NextSibling();
    while( iterator )
    {
        columnline = max( columnline, iterator->GetBoundaryRect().right );
        iterator = iterator->NextSibling();
    }

    return columnline;
}





/*
 * @brief: stream layout
 */
FaceStreamLayout::FaceStreamLayout()
{
}

bool FaceStreamLayout::BeforeBlockLayout
    (
      layout &parent_layout,
      FaceWindow &control,
      rect &boundary,
      rect &border,
      rect &content,
      layout &this_layout
    )
{
    if( control.style_.position_ == POSITION_ABSOLUTE )
    {
        return BeforeAbsoluteLayout( parent_layout, control, boundary, border, content, this_layout );
    }
    else
    {
        return BeforeRelativeLayout( parent_layout, control, boundary, border, content, this_layout );
    }
}

bool FaceStreamLayout::AfterBlockLayout
    (
      layout &parent_layout,
      FaceWindow &control,
      rect &boundary,
      rect &border,
      rect &content,
      layout &this_layout
    )
{
    if( control.style_.position_ == POSITION_ABSOLUTE )
    {
        return AfterAbsoluteLayout( parent_layout, control, boundary, border, content, this_layout );
    }
    else
    {
        return AfterRelativeLayout( parent_layout, control, boundary, border, content, this_layout );
    }
}

bool FaceStreamLayout::BeforeAbsoluteLayout
    (
      layout &parent_layout,
      FaceWindow &control,
      rect &boundary,
      rect &border,
      rect &content,
      layout &this_layout
    )
{
    boundary.empty();
    border.empty();
    content.empty();

    rect &rcParent = parent_layout.content;
    FaceStyle &style = control.style_;

    unit &MarginLeft    = style.margin_left_;
    unit &MarginTop     = style.margin_top_;
    unit &MarginRight   = style.margin_right_;
    unit &MarginBottom  = style.margin_bottom_;

    int &border_width_    = style.border_width_;

    unit &PaddingLeft   = style.padding_left_;
    unit &PaddingTop    = style.padding_top_;
    unit &PaddingRight  = style.padding_right_;
    unit &PaddingBottom = style.padding_bottom_;

    unit &Width         = style.width_;
    unit &Height        = style.height_;

    unit &Left          = style.left_;
    unit &Top           = style.top_;
    unit &Right         = style.right_;
    unit &Bottom        = style.bottom_;

    int maxWidth = rcParent.width();
    int maxHeight = rcParent.height();
    if( (parent_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        maxWidth = parent_layout.width;
    }
    if( (parent_layout.fit & FIT_TYPE_HEIGHT) == FIT_TYPE_HEIGHT )
    {
        maxHeight = parent_layout.height;
    }

    int width  = 2 * border_width_ + PaddingLeft( maxWidth ) + Width( maxWidth ) + PaddingRight( maxWidth );
    int height = 2 * border_width_ + PaddingTop( maxHeight ) + Height( maxHeight ) + PaddingBottom( maxHeight );

    rect rcBorder;
    rect rcBoundary;
    dword dwPositionType = LayoutHelper::GetPositionType( style );
    if( dwPositionType == POSITION_TYPE_LEFT_TOP )
    {
        rcBorder.left = rcParent.left + Left( maxWidth ) + MarginLeft( maxWidth );
        rcBorder.top = rcParent.top + Top( maxHeight ) + MarginTop( maxHeight );
        rcBorder.right = rcBorder.left + width;
        rcBorder.bottom = rcBorder.top + height;

        rcBoundary.left = rcParent.left + Left( maxWidth );
        rcBoundary.top = rcParent.top + Top( maxHeight );
        rcBoundary.right = rcBorder.right;
        rcBoundary.bottom = rcBorder.bottom;
    }
    else if( dwPositionType == POSITION_TYPE_LEFT_BOTTOM )
    {
        rcBorder.left = rcParent.left + Left( maxWidth ) + MarginLeft( maxWidth );
        rcBorder.bottom = rcParent.bottom - Bottom( maxHeight ) - MarginBottom( maxHeight );
        rcBorder.right = rcBorder.left + width;
        rcBorder.top = rcBorder.bottom - height;

        rcBoundary.left = rcParent.left + Left( maxWidth );
        rcBoundary.bottom = rcParent.bottom - Bottom( maxHeight );
        rcBoundary.right = rcBorder.right;
        rcBoundary.top = rcBorder.top;
    }
    else if( dwPositionType == POSITION_TYPE_RIGHT_TOP )
    {
        rcBorder.right = rcParent.right - Right( maxWidth ) - MarginRight( maxWidth );
        rcBorder.top = rcParent.top + Top( maxHeight ) + MarginTop( maxHeight );
        rcBorder.left = rcBorder.right - width;
        rcBorder.bottom = rcBorder.top + height;

        rcBoundary.right = rcParent.right - Right( maxWidth );
        rcBoundary.top = rcParent.top + Top( maxHeight );
        rcBoundary.left = rcBorder.left;
        rcBoundary.bottom = rcBorder.bottom;
    }
    else if( dwPositionType == POSITION_TYPE_RIGHT_BOTTOM )
    {
        rcBorder.right = rcParent.right - Right( maxWidth ) - MarginRight( maxWidth );
        rcBorder.bottom = rcParent.bottom - Bottom( maxHeight ) - MarginBottom( maxHeight );
        rcBorder.left = rcBorder.right - width;
        rcBorder.top = rcBorder.bottom - height;

        rcBoundary.right = rcParent.right - Right( maxWidth );
        rcBoundary.bottom = rcParent.bottom - Bottom( maxHeight );
        rcBoundary.left = rcBorder.left;
        rcBoundary.top = rcBorder.top;
    }

    boundary = rcBoundary;
    border = rcBorder;

    content.left = border.left + PaddingLeft( maxWidth ) + border_width_;
    content.right = border.right - PaddingRight( maxWidth ) - border_width_;
    content.top = border.top + PaddingTop( maxHeight ) + border_width_;
    content.bottom = border.bottom - PaddingBottom( maxHeight ) - border_width_;

    this_layout.content = content;
    this_layout.fit = LayoutHelper::GetFitType( style );
    if( (this_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        this_layout.width = maxWidth;
    }
    if( (this_layout.fit & FIT_TYPE_HEIGHT ) == FIT_TYPE_HEIGHT )
    {
        this_layout.height = maxHeight;
    }

    this_layout.row = content.top;
    this_layout.col = content.left;
    this_layout.baseline = content.top;
    this_layout.boundary.set( content.left, content.top, content.left, content.top );

    return true;
}

bool FaceStreamLayout::BeforeRelativeLayout
    (
      layout &parent_layout,
      FaceWindow &control,
      rect &boundary,
      rect &border,
      rect &content,
      layout &this_layout
    )
{
    boundary.empty();
    border.empty();
    content.empty();

    rect &rcParent = parent_layout.content;
    FaceStyle &style = control.style_;

    unit &MarginLeft    = style.margin_left_;
    unit &MarginTop     = style.margin_top_;
    unit &MarginRight   = style.margin_right_;
    unit &MarginBottom  = style.margin_bottom_;

    int &border_width_    = style.border_width_;

    unit &PaddingLeft   = style.padding_left_;
    unit &PaddingTop    = style.padding_top_;
    unit &PaddingRight  = style.padding_right_;
    unit &PaddingBottom = style.padding_bottom_;

    unit &Width         = style.width_;
    unit &Height        = style.height_;

    int maxWidth = rcParent.width();
    int maxHeight = rcParent.height();
    if( (parent_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        maxWidth = parent_layout.width;
    }
    if( (parent_layout.fit & FIT_TYPE_HEIGHT) == FIT_TYPE_HEIGHT )
    {
        maxHeight = parent_layout.height;
    }

    int width  = 2 * border_width_ + PaddingLeft( maxWidth ) + PaddingRight(maxWidth) + Width( maxWidth );
    int height = 2 * border_width_ + PaddingTop( maxHeight ) + PaddingBottom( maxHeight ) + Height( maxHeight );

    boundary.left = parent_layout.col;
    boundary.top = parent_layout.row;

    border.left = parent_layout.col + MarginLeft( maxWidth );
    border.top = parent_layout.row + MarginTop( maxHeight );
    border.right = border.left + width;
    border.bottom = border.top + height;

    /*
     * @brief: set the left and top boundary
     */
    parent_layout.boundary.left = min( parent_layout.boundary.left, border.left );
    parent_layout.boundary.top = min( parent_layout.boundary.top, border.top );

    content.left = border.left + PaddingLeft( maxWidth ) + border_width_;
    content.right = border.right - PaddingRight( maxWidth ) - border_width_;
    content.top = border.top + PaddingTop( maxHeight ) + border_width_;
    content.bottom = border.bottom - PaddingBottom( maxHeight ) - border_width_;

    this_layout.content = content;
    this_layout.fit = LayoutHelper::GetFitType( style );
    if( (this_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        this_layout.width = maxWidth;
    }
    if( (this_layout.fit & FIT_TYPE_HEIGHT ) == FIT_TYPE_HEIGHT )
    {
        this_layout.height = maxHeight;
    }

    this_layout.col = content.left;
    this_layout.row = content.top;
    this_layout.baseline = content.top;
    this_layout.boundary.set( content.left, content.top, content.left, content.top );

    return true;
}

bool FaceStreamLayout::AfterAbsoluteLayout
    (
      layout &parent_layout,
      FaceWindow &control,
      rect &boundary,
      rect &border,
      rect &content,
      layout &this_layout
    )
{
    if( this_layout.leaf )
    {
        return true;
    }

    FaceStyle &style = control.style_;

    int width = this_layout.boundary.right - this_layout.content.left;
    int height = this_layout.boundary.bottom - this_layout.content.top;
    int xOffset = 0;
    int yOffset = 0;

    if( (this_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        dword dwPositionType = LayoutHelper::GetPositionType( style );
        switch( dwPositionType )
        {
        case POSITION_TYPE_LEFT_TOP:
        case POSITION_TYPE_LEFT_BOTTOM:
            boundary.right += width;
            border.right += width;
            content.right += width;
            xOffset = 0;
            break;

        case POSITION_TYPE_RIGHT_TOP:
        case POSITION_TYPE_RIGHT_BOTTOM:
            boundary.left -= width;
            border.left -= width;
            content.left -= width;
            xOffset = -width;
            break;
        }
    }

    if( (this_layout.fit & FIT_TYPE_HEIGHT) == FIT_TYPE_HEIGHT )
    {
        dword dwPositionType = LayoutHelper::GetPositionType( style );
        switch( dwPositionType )
        {
        case POSITION_TYPE_LEFT_TOP:
        case POSITION_TYPE_RIGHT_TOP:
            boundary.bottom += height;
            border.bottom += height;
            content.bottom += height;
            yOffset = 0;
            break;

        case POSITION_TYPE_LEFT_BOTTOM:
        case POSITION_TYPE_RIGHT_BOTTOM:
            boundary.top -= height;
            border.top -= height;
            content.top -= height;
            yOffset = -height;
            break;
        }
    }

    if( xOffset != 0 || yOffset != 0 )
    {
        control.Offset( xOffset, yOffset );

        boundary.offset( -xOffset, -yOffset );
        border.offset( -xOffset, -yOffset );
        content.offset( -xOffset, -yOffset );
    }

    /*
     * @brief: children alignment
     */
    xOffset = 0;
    yOffset = 0;

    if( this_layout.boundary.left >= content.left &&
        this_layout.boundary.right <= content.right )
    {
        if( (style.horizontal_alignment_ & HORIZONTAL_ALIGNMENT_LEFT) == HORIZONTAL_ALIGNMENT_LEFT )
        {
            xOffset = this_layout.boundary.left - content.left;
        }
        else if( (style.horizontal_alignment_ & HORIZONTAL_ALIGNMENT_CENTER) == HORIZONTAL_ALIGNMENT_CENTER )
        {
            xOffset = ( border.width() - this_layout.boundary.width() ) / 2;
        }
        else if( (style.horizontal_alignment_ & HORIZONTAL_ALIGNMENT_RIGHT) == HORIZONTAL_ALIGNMENT_RIGHT )
        {
            xOffset = this_layout.boundary.right - content.right;
        }
    }

    if( this_layout.boundary.top >= content.top &&
        this_layout.boundary.bottom <= content.bottom )
    {
        if( (style.vertical_alignment_ & VERTICAL_ALIGNMENT_TOP) == VERTICAL_ALIGNMENT_TOP )
        {
            yOffset = content.top - this_layout.boundary.top;
        }
        else if( (style.vertical_alignment_ & VERTICAL_ALIGNMENT_MIDDLE) == VERTICAL_ALIGNMENT_MIDDLE )
        {
            yOffset = ( border.height() - this_layout.boundary.height() ) / 2;
        }
        else if( (style.vertical_alignment_ & VERTICAL_ALIGNMENT_BOTTOM) == VERTICAL_ALIGNMENT_BOTTOM )
        {
            yOffset = content.bottom - this_layout.boundary.bottom;
        }
    }

    if( xOffset != 0 || yOffset != 0 )
    {
        control.Offset( xOffset, yOffset );

        boundary.offset( -xOffset, -yOffset );
        border.offset( -xOffset, -yOffset );
        content.offset( -xOffset, -yOffset );
    }

    return true;
}

bool FaceStreamLayout::AfterRelativeLayout
    (
      layout &parent_layout,
      FaceWindow &control,
      rect &boundary,
      rect &border,
      rect &content,
      layout &this_layout
    )
{
    FaceStyle &style = control.style_;

    if( !this_layout.leaf )
    {
        int width = this_layout.boundary.right - this_layout.content.left;
        int height = this_layout.boundary.bottom - this_layout.content.top;

        if( (this_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
        {
            border.right += width;
            content.right += width;
        }
        if( (this_layout.fit & FIT_TYPE_HEIGHT) == FIT_TYPE_HEIGHT )
        {
            border.bottom += height;
            content.bottom += height;
        }
    }

    rect &rcParent = parent_layout.content;
    int maxWidth = rcParent.width();
    int maxHeight = rcParent.height();
    if( (parent_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        maxWidth = parent_layout.width;
    }
    if( (parent_layout.fit & FIT_TYPE_HEIGHT) == FIT_TYPE_HEIGHT )
    {
        maxHeight = parent_layout.height;
    }

    unit &MarginRight = style.margin_right_;
    unit &MarginBottom = style.margin_bottom_;

    parent_layout.col = border.right + MarginRight( maxWidth );
    parent_layout.baseline = max( parent_layout.baseline, border.bottom + MarginBottom( maxHeight ) );

    boundary.right = parent_layout.col;
    boundary.bottom = parent_layout.baseline;

    parent_layout.boundary.right = max( parent_layout.boundary.right, parent_layout.col );
    parent_layout.boundary.bottom = max( parent_layout.boundary.bottom, parent_layout.baseline );

    int xOffset = 0;
    int yOffset = 0;

    if( this_layout.boundary.left >= content.left &&
        this_layout.boundary.right <= content.right )
    {
        if( (style.horizontal_alignment_ & HORIZONTAL_ALIGNMENT_LEFT) == HORIZONTAL_ALIGNMENT_LEFT )
        {
            xOffset = this_layout.boundary.left - content.left;
        }
        else if( (style.horizontal_alignment_ & HORIZONTAL_ALIGNMENT_CENTER) == HORIZONTAL_ALIGNMENT_CENTER )
        {
            xOffset = ( border.width() - this_layout.boundary.width() ) / 2;
        }
        else if( (style.horizontal_alignment_ & HORIZONTAL_ALIGNMENT_RIGHT) == HORIZONTAL_ALIGNMENT_RIGHT )
        {
            xOffset = this_layout.boundary.right - content.right;
        }
    }

    if( this_layout.boundary.top >= content.top &&
        this_layout.boundary.bottom <= content.bottom )
    {
        if( (style.vertical_alignment_ & VERTICAL_ALIGNMENT_TOP) == VERTICAL_ALIGNMENT_TOP )
        {
            yOffset = content.top - this_layout.boundary.top;
        }
        else if( (style.vertical_alignment_ & VERTICAL_ALIGNMENT_MIDDLE) == VERTICAL_ALIGNMENT_MIDDLE )
        {
            yOffset = ( border.height() - this_layout.boundary.height() ) / 2;
        }
        else if( (style.vertical_alignment_ & VERTICAL_ALIGNMENT_BOTTOM) == VERTICAL_ALIGNMENT_BOTTOM )
        {
            yOffset = content.bottom - this_layout.boundary.bottom;
        }
    }

    if( xOffset != 0 || yOffset != 0 )
    {
        control.Offset( xOffset, yOffset );

        boundary.offset( -xOffset, -yOffset );
        border.offset( -xOffset, -yOffset );
        content.offset( -xOffset, -yOffset );
    }

    return true;
}

bool FaceStreamLayout::BeforeInlineLayout
    (
      layout &parent_layout,
      FaceWindow &control
    )
{
    FaceStyle &style = control.style_;

    unit &MarginLeft    = style.margin_left_;
    int &border_width_    = style.border_width_;
    unit &PaddingLeft   = style.padding_left_;

    int maxWidth = 0;
    int rest = 0;

    if( (parent_layout.fit & FIT_TYPE_WIDTH) == FIT_TYPE_WIDTH )
    {
        maxWidth = parent_layout.width;
        rest = parent_layout.width + parent_layout.content.left - parent_layout.col;
    }
    else
    {
        maxWidth = parent_layout.content.width();
        rest = parent_layout.content.right - parent_layout.col;
    }

    parent_layout.baseline = parent_layout.row + style.line_height_;

    int xSize = border_width_ + MarginLeft( maxWidth ) + PaddingLeft( maxWidth );
    while( xSize >= rest )
    {
        parent_layout.col = parent_layout.content.left;
        parent_layout.row += style.line_height_;
        parent_layout.baseline += style.line_height_;

        parent_layout.boundary.left = min( parent_layout.boundary.left, parent_layout.content.left );
        parent_layout.boundary.right = max( parent_layout.boundary.right, parent_layout.content.right );
        parent_layout.boundary.top = min( parent_layout.boundary.top, parent_layout.row );
        parent_layout.boundary.bottom = max( parent_layout.boundary.bottom, parent_layout.baseline );

        xSize -= rest;
        rest = maxWidth;
    }

    parent_layout.col += xSize;

    parent_layout.boundary.left = min( parent_layout.boundary.left, parent_layout.col );
    parent_layout.boundary.right = max( parent_layout.boundary.right, parent_layout.col );

    return true;
}

bool FaceStreamLayout::AfterInlineLayout
    (
      layout &parent_layout,
      FaceWindow &control
    )
{
    FaceStyle &style = control.style_;

    unit &MarginRight   = style.margin_right_;
    int &border_width_    = style.border_width_;
    unit &PaddingRight  = style.padding_right_;

    int maxWidth = 0;
    int rest = 0;
    if( (parent_layout.fit & FIT_TYPE_WIDTH ) == FIT_TYPE_WIDTH )
    {
        maxWidth = parent_layout.width;
        rest = parent_layout.width + parent_layout.content.left - parent_layout.col;
    }
    else
    {
        maxWidth = parent_layout.content.width();
        rest = parent_layout.content.right - parent_layout.col;
    }

    int xSize = border_width_ + MarginRight( maxWidth ) + PaddingRight(maxWidth);
    while( xSize >= rest )
    {
        parent_layout.col = parent_layout.content.left;
        parent_layout.row += style.line_height_;
        parent_layout.baseline += style.line_height_;

        parent_layout.boundary.left = min( parent_layout.boundary.left, parent_layout.content.left );
        parent_layout.boundary.right = max( parent_layout.boundary.right, parent_layout.content.right );
        parent_layout.boundary.top = min( parent_layout.boundary.top, parent_layout.row );
        parent_layout.boundary.bottom = max( parent_layout.boundary.bottom, parent_layout.baseline );

        xSize -= rest;
        rest = maxWidth;
    }

    parent_layout.col += xSize;

    parent_layout.boundary.left = min( parent_layout.boundary.left, parent_layout.col );
    parent_layout.boundary.right = max( parent_layout.boundary.right, parent_layout.col );

    return true;
}



};